package top.system.dept.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.system.dept.mapper.ApplyItemMapper;
import top.system.dept.mapper.ItemMapper;
import top.system.dept.mapper.ThingOrderMapper;
import top.system.dept.pojo.*;
import top.system.dept.utils.ConstantPool;
import top.system.dept.utils.MapUtil;
import top.system.dept.utils.RespUtil;
import top.system.dept.utils.StringUtil;

import java.io.Serializable;
import java.util.*;

/**
 * @ClassName: ThingOrderService
 * @Author: Amosen
 * @Since: 2021/5/21
 */

@Service
@Transactional(rollbackFor = {Exception.class})
public class ThingOrderService {

    Logger logger = LoggerFactory.getLogger(ThingOrderService.class);

    @Autowired
    UserService userService;

    @Autowired
    WechatService wechatService;

    @Autowired
    MailService mailService;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    ItemMapper itemMapper;

    @Autowired
    ApplyItemMapper applyItemMapper;

    @Autowired
    ThingOrderMapper orderMapper;

    // 工单号前缀
    @Value("${mini.item.order.id.prefix}")
    private String orderIdPrefix;

    // 工单号后缀
    @Value("${mini.item.order.id.suffix}")
    private String orderIdSuffix;

    @Value("${mini.item.add.check.notify.id}")
    private String applyItemNotifyId;
    @Value("${mini.item.add.check.page}")
    private String checkPage;
    @Value("${mini.item.add.check.notify.checkType.key}")
    private String checkTypeKey;
    @Value("${mini.item.add.check.notify.orderId.key}")
    private String orderIdKey;
    @Value("${mini.item.add.check.notify.time.key}")
    private String timeKey;
    @Value("${mini.item.add.check.notify.checkThing.key}")
    private String checkThingKey;
    @Value("${mini.item.add.check.notify.applyUser.key}")
    private String applyUserKey;

    @Value("${mini.item.order.prepare.notify.id}")
    private String prepareNotifyId;
    @Value("${mini.item.order.prepare.notify.orderId.key}")
    private String prepareOrderIdKey;
    @Value("${mini.item.order.prepare.notify.contactor.key}")
    private String prepareContactorKey;
    @Value("${mini.item.order.prepare.notify.donetime.key}")
    private String prepareDoneTimeKey;

    @Value("${mini.item.order.checkResult.notify.id}")
    private String checkResultNotifyId;
    @Value("${mini.item.order.checkResult.checkType.key}")
    private String checkResultCheckTypeKey;
    @Value("${mini.item.order.checkResult.orderId.key}")
    private String checkResultOrderIdKey;
    @Value("${mini.item.order.checkResult.result.key}")
    private String checkResultKey;
    @Value("${mini.item.order.checkResult.checkUser.key}")
    private String checkResultCheckUserKey;

    @Value("${mini.item.order.prepared.notify.id}")
    private String preparedNotifyId;
    @Value("${mini.item.order.prepared.type.key}")
    private String preparedTypeKey;
    @Value("${mini.item.order.prepared.orderid.key}")
    private String preparedOrderIdKey;
    @Value("${mini.item.order.prepared.handler.key}")
    private String preparedHandlerKey;
    @Value("${mini.item.order.prepared.doneTime.key}")
    private String preparedDoneTimeKey;
    @Value("${mini.item.order.prepared.result.key}")
    private String preparedResultKey;

    @Value("${mail.check.template}")
    private String checkOrderTemplate;

    @Value("${mail.assignment.template}")
    private String assignmentTemplate;

    @Value("${mail.stateChanged.template}")
    private String orderStateChangedTemplate;

    private static final String applyItemCheckType = "申请物资审批";

    @Value("${order.status.SUBMITTED}")
    public String SUBMITTED = "已提交";

    @Value("${order.status.PASSED}")
    public String PASSED = "审核已通过";

    @Value("${order.status.NO_PASS}")
    public String NO_PASS = "审核未通过";

    @Value("${order.status.PREPARING}")
    public String PREPARING = "已移交";

    @Value("${order.status.PREPARED}")
    public String PREPARED = "已准备";

    @Value("${order.status.RETURNED}")
    public String RETURNED = "已归还";

    @Value("${order.status.DONE}")
    public String DONE = "已完成";

    // 预处理订单，这个方法应当返回订单编号，或者返回错误信息
    @Transactional(rollbackFor = {Exception.class})
    public SysResp generateOrder(List<Map<String, Integer>> items, String userModal, Date useDate, Date returnDate) {
        // 检查items格式：[{"itemid": integer, "number": integer}]
        if (!checkItemsParam(items)) {
            return RespUtil.illegalParam();
        }

        // 检查用户是否登录
        User user = userService.getUserByModal(userModal);
        if (user == null) {
            return RespUtil.noLogin();
        }

        // 记录时间
        long applyTimestamp = System.currentTimeMillis();

        // 校验通过，生成订单
        List<ApplyItem> applyItems = new ArrayList<>();
        for (Map<String, Integer> item : items) {
            // 生成单个物品信息
            Item savedItem = itemMapper.getItemById(item.get("itemid")); // 从数据库中取出库存中的物品
            if (savedItem == null) { // 库存中不存在该物品
                return RespUtil.illegalParam().setMessage("The item you applied does not exists");
            }
            Integer applyNum = item.get("number");
            if (savedItem.getNumber() < applyNum) { // 申请物品数量不足
                return RespUtil.fail().setMessage("The item you applied is not enough");
            }

            // 装载单个订单信息
            ApplyItem applyItem = new ApplyItem();
            applyItem.load(savedItem, applyNum);
            applyItems.add(applyItem);

        }

        // 生成工单号，前缀 + 受理时间戳 + 后缀
        String orderId = orderIdPrefix + applyTimestamp + orderIdSuffix;
        Date applyDate = new Date(applyTimestamp);
        Dept dept = user.getDept();

        ThingOrder thingOrder = new ThingOrder(null, orderId, applyItems, applyDate, useDate, returnDate, dept, user, SUBMITTED);

        try {
            // 将订单先缓存到redis，当用户确定提交后保存到mysql
            ValueOperations ops = redisTemplate.opsForValue();
            // 以orderId为key，序列化的thingOrder为value
            ops.set(orderId, thingOrder, ConstantPool.ORDER_TIMEOUT, ConstantPool.ORDER_TIMEOUT_UNIT);
            // 返回用户订单信息
            return RespUtil.successData("order", thingOrder);
        } catch (Exception e) {
            logger.error("ThingOrder操作Redis失败！", e);
            return RespUtil.fail();
        }

    }

    // 处理确认提交订单
    @Transactional(rollbackFor = {Exception.class})
    public SysResp handleSubmitOrder(String userModal, String orderId) {
        // 检查用户是否登录
        User user = userService.getUserByModal(userModal);
        if (user == null) {
            return RespUtil.noLogin();
        }

        // 检查订单是否过期
        ValueOperations ops = redisTemplate.opsForValue();
        ThingOrder order;
        try {
            Object cachedOrder = ops.get(orderId);
            if (cachedOrder == null) {
                throw new ClassCastException();
            }
            order = (ThingOrder) cachedOrder;
        } catch (ClassCastException e) {
            return RespUtil.fail().setMessage("The order you submitted has passed away");
        } catch (Exception e) {
            logger.error("ThingOrder遇到异常", e);
            return RespUtil.fail();
        }

        // 检查订单中的申请用户和登录用户是否是同一人
        if (!order.getApplyUser().getAccount().equals(user.getAccount())) {
            return RespUtil.fail();
        }

        // 校验并更新数据库仓储信息
        List<Integer> itemIds = new ArrayList<>();
        for (ApplyItem applyItem : order.getItems()) {
            Integer savedItemId = applyItem.getiId();
            Item savedItem = itemMapper.getItemById(savedItemId);
            if (savedItem.getNumber() < applyItem.getNum()) { // 此时数据库中的仓储被另一个用户在当前用户提交订单前拿走了
                // 抛出异常但不捕捉，交由SpringBoot事务处理器回滚数据
                throw new RuntimeException();
            } else { // 此时更新数据库信息
                savedItem.setNumber(savedItem.getNumber() - applyItem.getNum());
                itemMapper.updateItem(savedItem);
                applyItemMapper.addApplyItem(applyItem);
                itemIds.add(applyItem.getId());
            }
        }

        // 将订单插入到数据库
        orderMapper.addOrder(order, itemIds);

//        // 下发通知给root管理员
//        new Thread(new SendNotify(order)).start();

        List<User> rootUsers = userService.getRootUser();
        Map<String, Object> templateData = MapUtil.obj2Map(order);

        for (User rootUser : rootUsers) {
            templateData.put("root", rootUser.getDescription());
            if (rootUser.getMail() != null) {
                new Thread(() -> {
                    mailService.sendMail("物资申请审批提醒",
                            rootUser.getMail(),
                            templateData,
                            checkOrderTemplate);
                }).start();
            }
        }

        return RespUtil.success();

    }

    public SysResp getThingOrders(boolean all, String userModal, String orderId) {

        if (all) {
            return allThingOrders(userModal);
        } else {
            return oneThingOrder(userModal, orderId);
        }

    }

    private SysResp allThingOrders(String userModal) {
        User user = userService.getUserByModal(userModal);
        if (user == null) {
            return RespUtil.noLogin();
        } else {
            List<ThingOrder> thingOrders = orderMapper.getThingOrdersByApplyUser(user.getId());
            return RespUtil.successData("orders", thingOrders);
        }
    }

    private SysResp oneThingOrder(String userModal, String orderId) {
        if (!StringUtil.strCanUse(orderId)) {
            return RespUtil.fail();
        } else {
            if (!userService.canUseManager(userModal)) {
                return RespUtil.fail();
            } else {
                ThingOrder thingOrder = orderMapper.getOrderByOrderId(orderId);
                return RespUtil.successData("order", thingOrder);
            }
        }
    }

    public SysResp getManageOrder(boolean all, String orderId, String userModal) {
        if (all) {
            return getAllManageOrder(userModal);
        } else {
            return getOneManageOrder(orderId, userModal);
        }
    }

    public SysResp getAllManageOrder(String userModal) {
        if (!userService.canUseManager(userModal)) {
            return RespUtil.fail();
        } else {
            List<ThingOrder> orders = orderMapper.getAllThingOrder();
            return RespUtil.successData("orders", orders);
        }
    }

    public SysResp getOneManageOrder(String orderId, String userModal) {
        User user = userService.getUserByModal(userModal);
        if (user == null) {
            return RespUtil.noLogin();
        } else {
            ThingOrder order = orderMapper.getOrderByOrderId(orderId);
            return RespUtil.successData("order", order);
        }
    }

    private boolean checkItemsParam(List<Map<String, Integer>> items) {
        for (Map<String, Integer> item : items) {
            try {
                Integer itemid = item.get("itemid");
                Integer number = item.get("number");
                if (itemid < 0 || number < 0) {
                    return false;
                }
            } catch (Exception e) {
                return false;
            }
        }
        return true;
    }

    // 处理审核结果
    @Transactional(rollbackFor = {Exception.class})
    public SysResp handleCheckResult(String userModal, String orderId, boolean passed, Integer toUserId) {
        // 检验审核通过后是否指定了下发通知用户
        if (passed && (toUserId == null || toUserId < 0)) {
            return RespUtil.illegalParam();
        }

        // 判断当前用户是否登录且是否具有ROOT权限审核工单
        if (!userService.canUseRoot(userModal)) { // 不具备权限或未登录
            return RespUtil.fail(); // 返回错误
        } else { // 具备权限
            // 获取订单信息
            ThingOrder order = orderMapper.getOrderByOrderId(orderId);
            if (!SUBMITTED.equals(order.getStatus())) { // 订单状态非刚提交状态，说明有其它root用户已经下发了通知
                return RespUtil.fail();
            } else { // 一切正常，处理逻辑
                User checkUser = userService.getUserByModal(userModal);
                if (passed) {
                    return handlePassedOrder(order, toUserId);
                } else {
                    return handleUnPassOrder(order, checkUser);
                }
            }
        }
    }

    private SysResp handlePassedOrder(ThingOrder order, Integer toUserId) {
        // 更新订单状态
        order.setStatus(PASSED);

        // 下发通知给部门成员准备物资
        User user = userService.getUserById(toUserId);
        if (user == null) {
            return RespUtil.fail();
        } else {
//            String openid = user.getOpenid();

            // 异步方式请求微信服务器发送消息
            // FIXME: 弃用微信服务器通知下发，改用邮件方式下发
//            new Thread(() -> {
//                // 封装数据
//                Map<String, String> datas = new HashMap<>();
//                datas.put(prepareOrderIdKey, order.getOrderId());
//                datas.put(prepareContactorKey, order.getApplyUser().getDescription());
//                datas.put(prepareDoneTimeKey, ConstantPool.sdf.format(order.getUseDate()));
//                String page = checkPage + "?orderType=" + orderIdSuffix + "&orderId=" + order.getOrderId();
//                wechatService.sendNotify(prepareNotifyId, openid, page, datas);
//            }).start();

            // 将工单更新到数据库
            orderMapper.updateOrderStatus(order);

            String mail = user.getMail();
            if (!StringUtil.strCanUse(mail)) {
                return RespUtil.fail().setMessage("该用户暂未绑定工作邮箱");
            }
            Map<String, Object> templateData = MapUtil.obj2Map(order);

            new Thread(() -> {
                mailService.sendMail("工单指派提醒", mail, templateData, assignmentTemplate);
            }).start();

            // 下发通知给申请用户，订单状态为已通过
//            new Thread(() -> {
//                // 封装数据
//                Map<String, String> datas = new HashMap<>();
//                datas.put(checkResultCheckTypeKey, "物资申请审核");
//                datas.put(checkResultOrderIdKey, order.getOrderId());
//                datas.put(checkResultKey, PASSED);
//                datas.put(checkResultCheckUserKey, user.getDescription());
//                wechatService.sendNotify(checkResultNotifyId, order.getApplyUser().getOpenid(), null, datas);
//            }).start();
            new Thread(() -> {
                mailService.sendMail("工单状态变更提醒", order.getApplyUser().getMail(), templateData, orderStateChangedTemplate);
            });
            return RespUtil.success();
        }
    }

    private SysResp handleUnPassOrder(ThingOrder order, User checkUser) {
        // 更改订单状态
        order.setStatus(NO_PASS);

        // 更新数据库
        ThingOrder savedOrder = orderMapper.getOrderByOrderId(order.getOrderId());
        if (savedOrder == null) {
            return RespUtil.fail();
        } else {
            // 将数据库中减少的数量加回来
            List<ApplyItem> applyItems = savedOrder.getItems();
            for (ApplyItem applyItem : applyItems) {
                Item savedItem = itemMapper.getItemById(applyItem.getiId());
                savedItem.setNumber(savedItem.getNumber() + applyItem.getNum());
                itemMapper.updateItem(savedItem);
            }
        }
        orderMapper.updateOrderStatus(order);

//        // 下发通知给申请人
//        new Thread(() -> {
//            Map<String, String> datas = new HashMap<>();
//            datas.put(checkResultCheckTypeKey, "物资申请审核");
//            datas.put(checkResultOrderIdKey, order.getOrderId());
//            datas.put(checkResultKey, NO_PASS);
//            datas.put(checkResultCheckUserKey, checkUser.getDescription());
//            wechatService.sendNotify(checkResultNotifyId, order.getApplyUser().getOpenid(), null, datas);
//        }).start();

        new Thread(() -> {
            Map<String, Object> templateData = MapUtil.obj2Map(order);
            mailService.sendMail("工单状态变更提醒", order.getApplyUser().getMail(), templateData, orderStateChangedTemplate);
        }).start();

        return RespUtil.success();
    }

    public SysResp handlePrepared(String orderId, String userModal, String result) {
        // 检查用户状态
        if (!userService.canUseManager(userModal)) { // 未登录或用户权限不足
            return RespUtil.fail();
        }

        // 获取处理人信息
        User handler = userService.getUserByModal(userModal);

        // 获取订单
        ThingOrder order = orderMapper.getOrderByOrderId(orderId);

        // 更新订单状态
        order.setStatus(PREPARED);
        orderMapper.updateOrderStatus(order);

//        // 下发通知给申请人
//        new Thread(() -> {
//            // 封装数据
//            long timestamp = System.currentTimeMillis();
//            String doneTime = ConstantPool.sdf.format(new Date(timestamp));
//            Map<String, String> datas = new HashMap<>();
//            datas.put(preparedTypeKey, "物资准备结果");
//            datas.put(preparedOrderIdKey, orderId);
//            datas.put(preparedHandlerKey, handler.getDescription());
//            datas.put(preparedDoneTimeKey, doneTime);
//            datas.put(preparedResultKey, result);
//            wechatService.sendNotify(preparedNotifyId, order.getApplyUser().getOpenid(), null, datas);
//        }).start();

        new Thread(() -> {
            Map<String, Object> templateData = MapUtil.obj2Map(order);
            mailService.sendMail("工单状态变更通知", order.getApplyUser().getMail(), templateData, orderStateChangedTemplate);
        }).start();

        return RespUtil.success();
    }

    public SysResp handleReturned(String orderId, String userModal, String result) {
        User user = userService.getUserByModal(userModal);
        if (user == null) { // 未登录
            return RespUtil.noLogin();
        }
        // 获取工单
        ThingOrder order = orderMapper.getOrderByOrderId(orderId);

        if (!user.getOpenid().equals(order.getApplyUser().getOpenid())) { // 不是本人提交的工单
            return RespUtil.fail();
        }

        // 更新订单状态
        order.setStatus(DONE);
        orderMapper.updateOrderStatus(order);

        // 更新仓储
        List<ApplyItem> applyItems = order.getItems();
        for (ApplyItem applyItem : applyItems) {
            Item savedItem = itemMapper.getItemById(applyItem.getiId());
            savedItem.setNumber(savedItem.getNumber() + applyItem.getNum());
            itemMapper.updateItem(savedItem);
        }

        // 下发通知给root用户
        final List<User> rootUsers = userService.getRootUser();

        new Thread(() -> {
            Map<String, Object> templateData = MapUtil.obj2Map(order);
            for (User rootUser : rootUsers) {
                mailService.sendMail("工单状态变更通知", order.getApplyUser().getMail(), templateData, orderStateChangedTemplate);
            }
        }).start();

//        new Thread(() -> {
//            // 封装下发数据
//            long timestamp = System.currentTimeMillis();
//            String doneTime = ConstantPool.sdf.format(new Date(timestamp));
//            Map<String, String> datas = new HashMap<>();
//            datas.put(preparedTypeKey, applyItemCheckType);
//            datas.put(preparedOrderIdKey, orderId);
//            datas.put(preparedHandlerKey, user.getDescription());
//            datas.put(preparedDoneTimeKey, doneTime);
//            datas.put(preparedResultKey, result);
//            for (User rootUser : rootUsers) {
//                try {
//                    wechatService.sendNotify(preparedNotifyId, rootUser.getOpenid(), null, datas);
//                } catch (Exception e) {
//                    logger.warn("发送微信通知失败！", e);
//                }
//            }
//        }).start();

        return RespUtil.success();
    }

    class SendNotify implements Runnable {

        private ThingOrder order;

        private String orderId;

        SendNotify(ThingOrder order) {
            this.order = order;
            this.orderId = order.getOrderId();
        }

        @Override
        public void run() {
            // 下发通知给root用户审核
            List<User> rootUsers = userService.getRootUser();
            // 封装数据
            Map<String, String> datas = new HashMap<>();
            datas.put(checkTypeKey, applyItemCheckType);
            datas.put(orderIdKey, orderId);
            datas.put(timeKey, ConstantPool.sdf.format(order.getApplyDate()));
            // 拼接申请物资
            String checkThing = "";
            for (ApplyItem item : order.getItems()) {
                if ((checkThing + item.getName() + "x" + item.getNum() + ";").length() > 20) {
                    break;
                } else {
                    checkThing = checkThing + item.getName() + "x" + item.getNum() + ";";
                }
            }
            checkThing = checkThing.substring(0, checkThing.length() - 1);

            datas.put(checkThingKey, checkThing);
            datas.put(applyUserKey, String.valueOf(order.getApplyUser().getDescription()));
            // 跳转页面
            String page = checkPage + "?orderType=" + orderIdSuffix + "&orderId=" + orderId;
            for (User rootUser : rootUsers) {
                wechatService.sendNotify(applyItemNotifyId, rootUser.getOpenid(), page, datas);
            }
        }
    }

}
